/*
 * MIT License
 *
 * Copyright (c) 2019 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package cn.seaboot.commons.core;

import cn.seaboot.commons.lang.P;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.function.Function;

/**
 * 日起格式化
 *
 * @author Mr.css
 * @version 2020-11-03 移除全部java.sql包的相关代码。（新系统完全弃用java.sql包的期）
 */
public class LocalDateUtils {
    private LocalDateUtils() {
    }

    public static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern(P.DATE_FORMAT_DATE);
    public static final DateTimeFormatter TIME = DateTimeFormatter.ofPattern(P.DATE_FORMAT_TIME);
    public static final DateTimeFormatter DATETIME = DateTimeFormatter.ofPattern(P.DATE_FORMAT_DATETIME);

    public static final DateTimeFormatter COMPACT_DATE = DateTimeFormatter.ofPattern(P.DATE_FORMAT_COMPACT_DATE);
    public static final DateTimeFormatter COMPACT_TIME = DateTimeFormatter.ofPattern(P.DATE_FORMAT_COMPACT_TIME);
    public static final DateTimeFormatter COMPACT_DATETIME = DateTimeFormatter.ofPattern(P.DATE_FORMAT_COMPACT_DATETIME);

    public static final DateTimeFormatter MONTH = DateTimeFormatter.ofPattern(P.DATE_FORMAT_MONTH);
    public static final DateTimeFormatter HOUR = DateTimeFormatter.ofPattern(P.DATE_FORMAT_HOUR);
    public static final DateTimeFormatter MINUTE = DateTimeFormatter.ofPattern(P.DATE_FORMAT_MINUTE);

    public static final DateTimeFormatter FULL_DATE_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    /**
     * long 类型转 date 格式字符串
     *
     * @param l long value
     * @return a String
     */
    public static String formatDate(long l) {
        return parseDateTime(l).format(DATE);
    }

    /**
     * Date 类型转 date 格式字符串
     *
     * @param date java.util.Date
     * @return a String
     */
    public static String formatDate(Date date) {
        return parseDateTime(date).format(DATE);
    }

    /**
     * LocalDate 类型转 date 格式字符串
     *
     * @param date LocalDate
     * @return a String
     */
    public static String formatDate(LocalDate date) {
        return date.format(DATE);
    }

    /**
     * long 类型转 time 格式字符串
     *
     * @param l long value
     * @return a String
     */
    public static String formatTime(long l) {
        return parseDateTime(l).format(TIME);
    }


    /**
     * LocalTime 类型转 time 格式字符串
     *
     * @param date LocalTime
     * @return a String
     */
    public static String formatTime(LocalTime date) {
        return date.format(TIME);
    }


    /**
     * Date 类型转 time 格式字符串
     *
     * @param date LocalDate
     * @return a String
     */
    public static String formatTime(Date date) {
        return parseDateTime(date).format(TIME);
    }

    /**
     * long 类型转 datetime 格式字符串
     *
     * @param l long value
     * @return a String
     */
    public static String formatDateTime(long l) {
        return parseDateTime(l).format(DATETIME);
    }

    /**
     * long 类型转 date 格式字符串
     *
     * @param date java.util.Date
     * @return a String
     */
    public static String formatDateTime(Date date) {
        return parseDateTime(date).format(DATE);
    }

    /**
     * LocalDateTime 类型转 datetime 格式字符串
     *
     * @param date LocalDateTime
     * @return a String
     */
    public static String formatDateTime(LocalDateTime date) {
        return date.format(DATETIME);
    }

    /**
     * long 类型转 date 格式字符串
     *
     * @param l long value
     * @return a String
     */
    public static String formatCompactDate(long l) {
        return parseDateTime(l).format(COMPACT_DATE);
    }

    /**
     * Date 类型转 date 格式字符串
     *
     * @param date java.util.Date
     * @return a String
     */
    public static String formatCompactDate(Date date) {
        return parseDateTime(date).format(COMPACT_DATE);
    }

    /**
     * LocalDate 类型转紧凑型 date 格式字符串
     *
     * @param date LocalDate
     * @return a String
     */

    public static String formatCompactDate(LocalDate date) {
        return date.format(COMPACT_DATE);
    }

    /**
     * long 类型转紧凑型 time 格式字符串
     *
     * @param l long value
     * @return a String
     */
    public static String formatCompactTime(long l) {
        return parseDateTime(l).format(COMPACT_TIME);
    }

    /**
     * Date 类型转紧凑型 time 格式字符串
     *
     * @param date java.util.Date
     * @return a String
     */
    public static String formatCompactTime(Date date) {
        return parseDateTime(date).format(COMPACT_TIME);
    }

    /**
     * LocalTime 类型转紧凑型 time 格式字符串
     *
     * @param date LocalTime
     * @return a String
     */
    public static String formatCompactTime(LocalTime date) {
        return date.format(COMPACT_TIME);
    }

    /**
     * long 类型转紧凑型 datetime 格式字符串
     *
     * @param l long value
     * @return a String
     */
    public static String formatCompactDateTime(long l) {
        return parseDateTime(l).format(COMPACT_DATETIME);
    }

    /**
     * long 类型转紧凑型 datetime 格式字符串
     *
     * @param date java.util.Date
     * @return a String
     */
    public static String formatCompactDateTime(Date date) {
        return parseDateTime(date).format(COMPACT_DATETIME);
    }


    /**
     * LocalDateTime 类型转紧凑型 datetime 格式字符串
     *
     * @param date LocalDateTime
     * @return a String
     */
    public static String formatCompactDateTime(LocalDateTime date) {
        return date.format(COMPACT_DATETIME);
    }

    /**
     * java.util.Date 类型转 LocalDateTime
     *
     * @param date java.util.Date
     * @return LocalDateTime
     */
    public static LocalDateTime parseDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), P.zoneId());
    }

    /**
     * Long 类型转 LocalDateTime
     *
     * @param l long value
     * @return LocalDateTime
     */
    public static LocalDateTime parseDateTime(long l) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(l), P.zoneId());
    }


    /**
     * 字符串转 LocalDateTime
     *
     * @param str string
     * @return LocalDateTime
     */
    public static LocalDateTime parseDateTime(String str) {
        return LocalDateTime.parse(str, DATETIME);
    }

    /**
     * UTC 时间转 LocalDateTime
     * <p>
     * E.G.: 2025-02-12T18:00:00.000Z
     * <p>
     * 注意：北京时间需要加 8 小时
     *
     * @param str string
     * @return LocalDateTime
     */
    public static LocalDateTime parseUTC(String str) {
        return OffsetDateTime.parse(str).toLocalDateTime();
    }

    /**
     * java.util.Date 类型转 LocalDate
     *
     * @param date java.util.Date
     * @return LocalDateTime
     */
    public static LocalDate parseDate(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), P.zoneId()).toLocalDate();
    }

    /**
     * Long 类型转 LocalDate
     *
     * @param l long value
     * @return LocalDateTime
     */
    public static LocalDate parseDate(long l) {
        return parseDateTime(l).toLocalDate();
    }

    /**
     * 字符串转 LocalDate
     *
     * @param str string
     * @return LocalDate
     */
    public static LocalDate parseDate(String str) {
        return LocalDate.parse(str, DATE);
    }

    /**
     * java.util.Date 类型转 LocalTime
     *
     * @param date java.util.Date
     * @return LocalDateTime
     */
    public static LocalTime parseTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), P.zoneId()).toLocalTime();
    }

    /**
     * Long 类型转 LocalTime
     *
     * @param l long value
     * @return LocalDateTime
     */
    public static LocalTime parseTime(long l) {
        return parseDateTime(l).toLocalTime();
    }

    /**
     * 字符串转 LocalTime
     *
     * @param str string
     * @return LocalTime
     */
    public static LocalTime parseTime(String str) {
        return LocalTime.parse(str, DATE);
    }


    /**
     * 紧凑型字符串转 LocalDate
     *
     * @param str string
     * @return Date
     */
    public static LocalDate parseCompactDate(String str) {
        return LocalDate.parse(str, COMPACT_DATE);
    }

    /**
     * 紧凑型字符串转 LocalTime
     *
     * @param str string
     * @return Date
     */
    public static LocalTime parseCompactTime(String str) {
        return LocalTime.parse(str, COMPACT_TIME);
    }

    /**
     * 紧凑型字符串转 LocalDateTime
     *
     * @param str string
     * @return Date
     */
    public static LocalDateTime parseCompactDateTime(String str) {
        return LocalDateTime.parse(str, COMPACT_DATETIME);
    }

    /**
     * Object 类型转 LocalDate
     *
     * @param obj obj value
     * @return LocalDateTime
     */
    public static LocalDate parseDate(Object obj) {
        if (obj == null) {
            return null;
        } else if (obj instanceof LocalDate) {
            return (LocalDate) obj;
        } else if (obj instanceof String) {
            return parseDate((String) obj);
        } else if (obj instanceof Long) {
            return parseDate((long) obj);
        } else if (obj instanceof Date) {
            return parseDate((Date) obj);
        } else if (obj instanceof LocalDateTime) {
            return ((LocalDateTime) obj).toLocalDate();
        } else {
            throw new ClassCastException(obj + " to LocalDate class case failed!");
        }
    }


    /**
     * Object 类型转 LocalDate
     *
     * @param obj obj value
     * @return LocalDateTime
     */
    public static LocalTime parseTime(Object obj) {
        if (obj == null) {
            return null;
        } else if (obj instanceof LocalTime) {
            return (LocalTime) obj;
        } else if (obj instanceof String) {
            return parseTime((String) obj);
        } else if (obj instanceof Long) {
            return parseTime((long) obj);
        } else if (obj instanceof Date) {
            return parseTime((Date) obj);
        } else if (obj instanceof LocalDateTime) {
            return ((LocalDateTime) obj).toLocalTime();
        } else {
            throw new ClassCastException(obj + " to LocalTime class case failed!");
        }
    }


    /**
     * Object 类型转 LocalDate
     *
     * @param obj obj value
     * @return LocalDateTime
     */
    public static LocalDateTime parseDateTime(Object obj) {
        if (obj == null) {
            return null;
        } else if (obj instanceof LocalDateTime) {
            return (LocalDateTime) obj;
        } else if (obj instanceof String) {
            return parseDateTime((String) obj);
        } else if (obj instanceof Long) {
            return parseDateTime((long) obj);
        } else if (obj instanceof Date) {
            return parseDateTime((Date) obj);
        } else if (obj instanceof LocalDate) {
            return LocalDateTime.of((LocalDate) obj, LocalTime.MIN);
        } else {
            throw new ClassCastException(obj + " to LocalDateTime class case failed!");
        }
    }

    /**
     * 获取时间的季节
     *
     * @return 季节(0春 / 1夏 / 2秋 / 3冬)
     */
    public int getSeason(LocalDate date) {
        return (date.getMonthValue() - 1) / 3;
    }

    /**
     * 遍历一个月中的每一天
     *
     * @param localDate date
     * @param function  callback
     * @param <T>       -
     */
    public static <T> void eachDayOfMonth(LocalDate localDate, Function<LocalDate, T> function) {
        localDate = localDate.with(TemporalAdjusters.firstDayOfMonth());
        int month = localDate.getMonthValue();
        while (month == localDate.getMonthValue()) {
            function.apply(localDate);
            localDate = localDate.plusDays(1);
        }
    }


    /**
     * 遍历一年中每一个月
     *
     * @param localDate date
     * @param function  callback
     * @param <T>       -
     */
    public static <T> void eachMonthOfYear(LocalDate localDate, Function<LocalDate, T> function) {
        localDate = localDate.with(TemporalAdjusters.firstDayOfYear());
        int year = localDate.getYear();
        while (year == localDate.getYear()) {
            function.apply(localDate);
            localDate = localDate.plusMonths(1);
        }
    }
}
